/*
 * LIST_EMBEDED		:
 * LIST_MULTITHREAD	: 
 *
 * LIST_NODE Funcs	:
 *	. list_node_set()
 *	. list_node_init()
 * 	. list_node_create()
 *	. list_node_create_set()
 *	. list_node_create_copy_set()
 *	. list_node_free()
 *
 *
 * LIST_ROOT Funcs	:
 *	.
 *	.
 *	.
 *	.
 *
 *
 *
 *
 *
 *
 *
 *
 */

#ifndef __LIST_H__
#define __LIST_H__

/* BEGIN INCLUDED FILES */
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#ifdef LIST_MULTITHREAD
#include <pthread.h>
#endif

#include "list_utils.h"
/* END   INCLUDED FILES */

/* BEGIN MAKE COMPATIBLE WITH C++ */
#ifdef __cplusplus
extern "C" {
#endif

/* BEGIN DEFINATION OF BUILDING ATTRIBUTES */

/** BEGIN LIST IMPLEMENTED BY MACROS **/
#if defined LIST_MACRO
	#define INLINE 
/** END   LIST IMPLEMENTED BY MACROS **/

/** BEGIN LIST IMPLEMENTED BY INLINE FUNCTIONS **/
#elif defined LIST_INLINE
	#define INLINE inline
/** END   LIST IMPLEMENTED BY INLINE FUNCTIONS **/

/** BEGIN LIST IMPLEMENTED BY FUNCTIONS **/
#elif defined LIST_FUNC
	#define INLINE
/** END   LIST IMPLEMENTED BY FUNCTIONS **/
#else 
	#define INLINE
#endif

/* END   DEFINATION OF BUILDING ATTRIBUTES */

/* BEGIN IMPLEMENT */

#define LIST_NODE_TYPE_NONE -1

/** BEGIN DEFINATION OF LIST_NODE STRUCTURE **/

/*** 
                             LIST_NODE STRUCTURE
+--------+--------+--------+--------+--------+--------+--------+-----------+            
| prev_p | next_p | root_p | data_p |  size  |  type  |  deep  | node_lock |
+--------+--------+--------+--------+--------+--------+--------+-----------+

                                 Figure. 1 
***/
struct _list_root ;
typedef struct _list_node {

	/*** it's the first item in the root for ever ***/
	struct _list_node * prev_p;
	/*** it's the last  item in the root for ever ***/
	struct _list_node * next_p; 
	/*** it's pointer of root ***/
	struct _list_root * root_p;
	/*** the pointer to data content ***/
	void * data_p;
	/*** the size of data content ***/
	unsigned int size;
	/*** the type of data content ***/
	int type;
	/*** deep copying or not ***/
	bool deep;
	/*** lock for handing node within multithread ***/
	#ifdef LIST_MULTITHREAD
	pthread_mutex_t node_lock;
	#endif
/*** using compact form for embeded system ***/
#ifdef LIST_PACKED
} __attrib__((__packed__)) LIST_NODE;
#else
} LIST_NODE;
#endif
/** END   DEFINATION OF LIST_NODE STRUCTURE **/

/** BEGIN DEFINATION OF LIST_ROOT STRUCTURE **/

/*** 
                       LIST_ROOT STRUCTURE
     +---------+-------------+------------+-----------+            
     |  head_p | curr_next_p | node_count | list_lock |
     +---------+-------------+------------+-----------+

                           Figure. 2 
***/
typedef struct _list_root {
	
	/*** pointer to first node in the list ***/
	LIST_NODE * head_p ;
	/*** the count of nodes in the list ***/
	unsigned int node_count;
	/*** lock for handing list within multithread ***/
	#ifdef LIST_MULTITHREAD
	pthread_mutex_t list_lock;
	#endif
/*** using compact form for embeded system ***/
#ifdef LIST_EMBEDED
} __attrib__((__packed__)) LIST_ROOT;
#else
} LIST_ROOT;
#endif
/** END   DEFINATION OF LIST_NODE STRUCTURE **/

/** BEGIN DEFINATION OF ERROR MESSAGE **/

typedef enum {
	LIST_SUCCESS = 0,
	LIST_ERROR ,
	LIST_MSG_COUNT ,
} LIST_STAT ;


typedef struct {
	int  stat;
	char * stat_msg;
} STAT_MSG ;

static STAT_MSG list_errmsg[] = {
	{ LIST_SUCCESS , "success !" },
	{ LIST_ERROR   , "error !" },
	{ LIST_MSG_COUNT , "" }
};

/*** ***/
inline char * list_stats_msg( LIST_STAT stat){
	if( list_errmsg[-stat].stat == stat ){
		return list_errmsg[ -stat ].stat_msg;
	}
	return NULL;
}
/*** ***/

/** END   DEFINATION OF ERROR MESSAGE **/

/** BEGIN DEFINATION OF INITIALIZER OF LIST_NODE **/
#ifdef LIST_MULTITHREAD
	#define LIST_NODE_INITIALIZER( NODE_P )	{ (NODE_P) , (NODE_P) , NULL , NULL , 0 , LIST_NODE_TYPE_NONE , false , PTHREAD_MUTEX_INITIALIZER }
#else  
	#define LIST_NODE_INITIALIZER( NODE_P )	{ (NODE_P) , (NODE_P) , NULL , NULL , 0 , LIST_NODE_TYPE_NONE , false }
#endif
/** END   DEFINATION OF INITIALIZER OF LIST_NODE **/

/** BEGIN DEFINATION OF VARIABLE **/
#ifdef LIST_MULTITHREAD
#define LIST_ROOT_INIT( ROOT )		\
	LIST_ROOT ROOT= { NULL , 0 , PTHREAD_MUTEX_INITIALIZER };
#else
#define LIST_ROOT_INIT( ROOT ) 		\
	LIST_ROOT ROOT= { NULL , 0 }; 			
#endif
/** END   DEFINATION OF VARIABLE **/

/** BEGIN DECLARATION OF VARIABLE **/
#define EXTERN_LIST_ROOT( ROOT )	\
	extern LIST_ROOT ROOT;
/** END   DECLARATION OF VARIABLE **/

/** BEGIN IMPLEMENTATION OF FUNCTIONS **/

/*** BEGIN IMPLEMENTATION FOR LIST_NODE FUNCS ***/

/**** this functions alway return 0 ****/
INLINE LIST_NODE * _list_node_set_parm(LIST_NODE * node_p ,
				LIST_ROOT * root_p ,
				void * data_p ,
				unsigned int size , 
				int type ,
				bool deep ){
	node_p->root_p = root_p ;
	node_p->data_p = data_p ; 
	node_p->size = size ;
	node_p->type = type ;
	node_p->deep = deep ; 

	return node_p ;
}

INLINE LIST_NODE * _list_node_set_parm_deep(	LIST_NODE * node_p ,
						LIST_ROOT * root_p ,
						const void * const data_p , 
						unsigned int size , 
						int type ){

	void * app_data_p;
	app_data_p = malloc_check_copy( size , data_p );

	return _list_node_set_parm( node_p , root_p , app_data_p , size , type , true );
}

INLINE LIST_NODE * _list_node_set_ptr(	LIST_NODE * node_p , 
					LIST_NODE * prev_p ,
					LIST_NODE * next_p ){
	node_p->prev_p = prev_p;
	node_p->next_p = next_p;

	return node_p ;
}

INLINE int _list_node_init( LIST_NODE * node_p ){

	_list_node_set_parm( node_p , NULL , NULL , 0 , LIST_NODE_TYPE_NONE , true );
	_list_node_set_ptr( node_p , node_p , node_p );

	#ifdef LIST_MULTITHREAD
	pthread_mutex_init( &(node_p->node_lock) , NULL);
	#endif

	return 0;
}

INLINE LIST_NODE * _list_node_create(void){

	LIST_NODE * node_p ;
	node_p = ( LIST_NODE *) malloc_check ( sizeof( LIST_NODE ) );

	_list_node_init( node_p );

	return node_p;
}

INLINE LIST_NODE * _list_node_copy( LIST_NODE * node_src_p ){

	LIST_NODE * node_dst_p ;

	node_dst_p = _list_node_create();

	_list_node_set_ptr( node_dst_p , node_src_p->prev_p , node_src_p->next_p );
	_list_node_set_parm_deep( node_dst_p , node_src_p->root_p , node_src_p->data_p , node_src_p->size , node_src_p->type );

	return node_dst_p;

}

INLINE void _list_node_free( LIST_NODE * node_p ){

	#ifdef LIST_MULTITHREAD
	pthread_mutex_destroy( &(node_p->node_lock) );
	#endif

	if( node_p->deep ){
		free( node_p->data_p );
	}

	_list_node_set_ptr( node_p , NULL , NULL );
	_list_node_set_parm( node_p , NULL , NULL , 0 , LIST_NODE_TYPE_NONE , false );

	free( node_p );

	return;	
}

/****              TOPOLOGY OF _list_node_insert_before() FUNC
                                                                                |
    prev_std_node_p                                          std_node_p         |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-----------------------------+  prev_p  |  next_p ||  |
|+----------+----+----+|                              |+----------+---------+|  |
+----------------|-----+                              +----------------------+  |
                 |                                        ^                     |
                 |                                        |                     |
                 +----------------------------------------+                     |
                                     node_p                                     |
                           +----------------------+                             |
                           |+----------+---------+|                             |
                           ||  prev_p  |  next_p ||                             |
                           |+----------+---------+|                             |
                           +----------------------+                             |
                                                                                |
                                                                                |
    prev_std_node_p                                          std_node_p         |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-------+          +-------> ||  prev_p  |  next_p ||  |
|+----------+----+----+|         |          |         |+-----+----+---------+|  |
+----------------|-----+         |          |         +------|---------------+  |
                 |               |   node_p |                |                  |
                 |         +-----|----------|-----+          |                  |
                 |         |+----+-----+----+----+|          |                  |
                 +-------> ||  prev_p  |  next_p || <--------+                  |
                           |+----------+---------+|                             |
                           +----------------------+                             |
                                                                                |
                                  Figure. 3                                     |
****/
INLINE LIST_NODE * _list_node_insert_before( LIST_NODE * std_node_p , LIST_NODE * node_p ){

	if( std_node_p ){
		node_p->next_p = std_node_p;
		node_p->prev_p = std_node_p->prev_p;

		std_node_p->prev_p->next_p = node_p;
		std_node_p->prev_p = node_p;
	}
	return node_p ;
}


/****              TOPOLOGY OF _list_node_insert_after() FUNC
                                                                                |
      std_node_p                                           next_std_node_p      |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-----------------------------+  prev_p  |  next_p ||  |
|+----------+----+----+|                              |+----------+---------+|  |
+----------------|-----+                              +----------------------+  |
                 |                                        ^                     |
                 |                                        |                     |
                 +----------------------------------------+                     |
                                     node_p                                     |
                          +-----------------------+                             |
                          | +----------+---------+|                             |
                          | |  prev_p  |  next_p ||                             |
                          | +----------+---------+|                             |
                          +-----------------------+                             |
                                                                                |
                                                                                |
      std_node_p                                           next_std_node_p      |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-------+          +-------> ||  prev_p  |  next_p ||  |
|+----------+----+----+|         |          |         |+-----+----+---------+|  |
+----------------|-----+         |          |         +------|---------------+  |
                 |               |   node_p |                |                  |
                 |         +-----|----------|-----+          |                  |
                 |         |+----+-----+----+----+|          |                  |
                 +-------> ||  prev_p  |  next_p || <--------+                  |
                           |+----------+---------+|                             |
                           +----------------------+                             |
                                                                                |
                                  Figure. 4                                     |
****/
INLINE LIST_NODE * _list_node_insert_after( LIST_NODE * std_node_p , LIST_NODE * node_p ){

	if( std_node_p ){
		node_p->prev_p = std_node_p;
		node_p->next_p = std_node_p->next_p;

		std_node_p->next_p->prev_p = node_p;
		std_node_p->next_p = node_p;
	}

	return node_p;
}

/****              TOPOLOGY OF _list_node_remove() FUNC
                                                                                |
    prev_node_p                                           next_node_p           |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-------+          +-------> ||  prev_p  |  next_p ||  |
|+----------+----+----+|         |          |         |+-----+----+---------+|  |
+----------------|-----+         |          |         +------|---------------+  |
                 |               |   node_p |                |                  |
                 |         +-----|----------|-----+          |                  |
                 |         |+----+-----+----+----+|          |                  |
                 +-------> ||  prev_p  |  next_p || <--------+                  |
                           |+----------+---------+|                             |
                           +----------------------+                             |
                                                                                |
    prev_node_p                                           next_node_p           |
+----------------------+                              +----------------------+  |
|+----------+---------+|                              |+----------+---------+|  |
||  prev_p  |  next_p || <-----------------------------+  prev_p  |  next_p ||  |
|+----------+----+----+|                              |+----------+---------+|  |
+----------------|-----+                              +----------------------+  |
                 |                                        ^                     |
                 |                                        |                     |
                 +----------------------------------------+                     |
                                     node_p                                     |
                           +----------------------+                             |
                           |+----------+---------+|                             |
                           ||  prev_p  |  next_p ||                             |
                           |+----------+---------+|                             |
                           +----------------------+                             |
                                                                                |
                                                                                |
                                  Figure. 5                                     |
****/
INLINE LIST_NODE * _list_node_remove( LIST_NODE * node_p ){

	/**** it's the only one node in the list ****/
	if( node_p->prev_p == node_p || node_p->next_p == node_p ){
		return NULL;
	}

	node_p->prev_p->next_p = node_p->next_p;
	node_p->next_p->prev_p = node_p->prev_p;

	return node_p->next_p ;
}

/*** END   IMPLEMENTATION FOR LIST_NODE FUNCS ***/

/*** BEGIN IMPLEMENTATION FOR LIST_ROOT FUNCS ***/

INLINE unsigned int _list_root_inc_count( LIST_ROOT * root_p ){
	return ++root_p->node_count;
}

INLINE unsigned int _list_root_dec_count( LIST_ROOT * root_p ){
	return --root_p->node_count;
}

INLINE unsigned int _list_root_count( LIST_ROOT * root_p ){
	return root_p->node_count;
}

INLINE void _list_root_set( 	LIST_ROOT * root_p , 
				LIST_NODE * head_p , 
				unsigned int node_count ){

	root_p->head_p = head_p;
	root_p->node_count = node_count;

	return;
}

INLINE int _list_root_init( LIST_ROOT * root_p ){

	_list_root_set( root_p , NULL , 0 );

	#ifdef LIST_MULTITHREAD
	pthread_mutex_init( &(root_p->list_lock) , NULL);
	#endif
	
	return 0;
}

INLINE LIST_NODE * _list_root_push_front( LIST_ROOT * root_p , LIST_NODE * node_p ){

	root_p->head_p = _list_node_insert_before( root_p->head_p , node_p );
	_list_root_inc_count( root_p );

	return node_p ;
}

INLINE LIST_NODE * _list_root_push_back( LIST_ROOT * root_p , LIST_NODE * node_p ){

	LIST_NODE * ret_node_p = _list_node_insert_before( root_p->head_p , node_p );
	if( root_p->head_p ){
		root_p->head_p = ret_node_p;
	}

	_list_root_inc_count( root_p );

	return node_p ;
}

INLINE LIST_NODE * _list_root_pop_front( LIST_ROOT * root_p ){

	LIST_NODE * ret_node_p = NULL;

	if( root_p->head_p ){
		ret_node_p = root_p->head_p ;
		root_p->head_p = _list_node_remove( root_p->head_p );
		_list_root_dec_count( root_p );
	}

	return ret_node_p ;		
}

INLINE LIST_NODE * _list_root_pop_back( LIST_ROOT * root_p ){

	LIST_NODE * ret_node_p = NULL;

	if( root_p->head_p ){
		ret_node_p = root_p->head_p->prev_p;
		if( !_list_node_remove( root_p->head_p->prev_p ) ){
			root_p->head_p = NULL;
		}
		_list_root_dec_count( root_p );
	}
	
	return ret_node_p ;
}

typedef void (* LIST_FUNC )( LIST_ROOT * root_p , LIST_NODE * node_p , void * data_p );

INLINE void _list_root_foreach( LIST_ROOT * root_p , LIST_FUNC func_p , void * data_p ){

	LIST_NODE * node_p = root_p->head_p;
	LIST_NODE * head_node_p = root_p->head_p;
	LIST_NODE * next_node_p;

	if( node_p ){

		do{
			next_node_p = node_p->next_p ;
			func_p( root_p , node_p , data_p );
			node_p = next_node_p ;

		}while( node_p != head_node_p && root_p->head_p );
	}
	return  ;
}

INLINE void _list_root_foreach_converse( LIST_ROOT * root_p , LIST_FUNC func_p , void * data_p ){

	LIST_NODE * node_p = root_p->head_p;
	LIST_NODE * head_node_p = root_p->head_p;
	LIST_NODE * prev_node_p ;

	if( node_p ){

		do{
			prev_node_p = node_p->prev_p ;
			func_p( root_p , node_p , data_p );
			node_p = prev_node_p ;

		}while( node_p != head_node_p && root_p->head_p );
	}
	return  ;
}

INLINE LIST_NODE * _list_root_insert_node_before( LIST_ROOT * root_p , LIST_NODE * std_node_p , LIST_NODE * node_p ){

	LIST_NODE * ret_node_p = _list_node_insert_before( std_node_p , node_p );
	if( root_p->head_p == std_node_p ){
		root_p->head_p = ret_node_p ;
	}
	_list_root_inc_count( root_p );
	return ;
}

INLINE LIST_NODE * _list_root_insert_node_after( LIST_ROOT * root_p , LIST_NODE * std_node_p , LIST_NODE * node_p ){

	_list_node_insert_after( std_node_p , node_p );
	_list_root_inc_count( root_p );

	return ;
}

INLINE LIST_NODE * _list_root_remove_node( LIST_ROOT * root_p , LIST_NODE * node_p ){

	LIST_NODE * ret_node_p = _list_node_remove( node_p );

	if( node_p == root_p->head_p ){
		root_p->head_p = ret_node_p ;
	}

	_list_root_dec_count( root_p );	
	return ret_node_p;

}

INLINE LIST_NODE * _list_root_del_node( LIST_ROOT * root_p , LIST_NODE * node_p ){
	
	_list_root_remove_node( root_p , node_p );
	_list_node_free( node_p );
	return node_p;
}

INLINE void _list_root_del_node_inner( LIST_ROOT * root_p , LIST_NODE * node_p , void * data_p ){

	_list_root_del_node( root_p , node_p );
	return ;
}

INLINE void _list_root_erase( LIST_ROOT * root_p ){

	_list_root_foreach( root_p , _list_root_del_node_inner , NULL );
	
	return ;
}

INLINE LIST_ROOT * _list_root_create(void){

	LIST_ROOT * root_p ;

	root_p = ( LIST_ROOT * )malloc_check( sizeof(LIST_ROOT) );
	_list_root_init( root_p );

	return root_p ;
}

INLINE void _list_root_free( LIST_ROOT * root_p ){

	#ifdef LIST_MULTITHREAD
	pthread_mutex_destroy( &(root_p->list_lock) );
	#endif

	_list_root_erase( root_p );

	_list_root_set( root_p , NULL , 0 );

	free( root_p );

	return ;
}

/*** END   IMPLEMENTATION FOR LIST_ROOT FUNCS ***/

/** END   IMPLEMENTATION OF FUNCTIONS **/

/* END   IMPLEMENT */

/* END   MAKE COMPATIBLE WITH C++ */
#ifdef __cplusplus
}
#endif

#endif
